package edu.suda.basic.media;

import java.util.Iterator;
import java.awt.*;
import java.awt.event.*;
import java.text.DecimalFormat;

import javax.swing.*;

import no.geosoft.cc.geometry.Geometry;
import no.geosoft.cc.util.NiceNumbers;
import no.geosoft.cc.util.NiceNumber;
import no.geosoft.cc.graphics.*;



/**
 * G demo program. Demonstrates:
 *
 * <ul>
 * <li>A rudimentary chart library
 * <li>The use of multiple scenes
 * <li>World extent usage
 * <li>Zooming and scrolling
 * <li><em>Nice number</em> generation
 * </ul>
 * 
 * @author <a href="mailto:jacob.dreyer@geosoft.no">Jacob Dreyer</a>
 */   
public class Demo15 extends JFrame
{
  /**
   * Class for creating the demo canvas and hande Swing events.
   */   
  public Demo15()
  {
    super ("G Graphics Library - Demo 15");
    setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE);
    
    // Create the GUI
    JPanel topLevel = new JPanel();
    topLevel.setLayout (new BorderLayout());
    getContentPane().add (topLevel);        

    JScrollBar hScrollBar = new JScrollBar (JScrollBar.HORIZONTAL);
    getContentPane().add (hScrollBar, BorderLayout.SOUTH);

    JScrollBar vScrollBar = new JScrollBar (JScrollBar.VERTICAL);
    getContentPane().add (vScrollBar, BorderLayout.EAST);

    // Create the graphic canvas
    GWindow window = new GWindow (new Color (255, 255, 255));
    topLevel.add (window.getCanvas(), BorderLayout.CENTER);    
    
    // Create a value set to be plotted
    ValueSet valueSet = new ValueSet();

    // Definition of exact chart location inside window
    Insets insets = new Insets (80, 60, 20, 20);
    
    // Create a "background" device oriented annotation scene
    GScene annotationScene = new GScene (window);
    GObject annotation = new Annotation (insets);
    annotationScene.add (annotation);

    // Create a value specific "plot" scene
    GScene plot = new Plot (window, valueSet, insets);
    annotationScene.setUserData (plot);

    pack();
    setSize (new Dimension (500, 500));
    setVisible (true);

    // Start zoom interaction
    GStyle zoomStyle = new GStyle();
    zoomStyle.setForegroundColor (new Color (0, 0, 0));
    zoomStyle.setBackgroundColor (new Color (0.8f, 1.0f, 0.8f, 0.3f));
    window.startInteraction (new ZoomInteraction (plot, zoomStyle));

    // Make sure plot can be scrolled
    plot.installScrollHandler (hScrollBar, vScrollBar);
  }



  
  private class Annotation extends GObject
  {
    private Insets    insets_;
    private GSegment  background_;
    private GSegment  title_;

    
    public Annotation (Insets insets)
    {
      insets_ = insets;
      
      background_ = new GSegment();
      GStyle backgroundStyle = new GStyle();
      backgroundStyle.setBackgroundColor (new Color (1.0f, 1.0f, 0.9f, 0.8f));
      background_.setStyle (backgroundStyle);
      addSegment (background_);

      title_ = new GSegment();
      GStyle titleStyle = new GStyle();
      titleStyle.setForegroundColor (new Color (100, 120, 120));
      titleStyle.setFont (new Font ("Dialog", Font.BOLD, 20));
      title_.setStyle (titleStyle);
      title_.setText (new GText ("G Graphics Library - Demo 15"));
      addSegment (title_);
      
      GStyle axisStyle = new GStyle();
      axisStyle.setForegroundColor (new Color (100, 100, 100));
      axisStyle.setBackgroundColor (null);
      axisStyle.setFont (new Font ("Dialog", Font.BOLD, 10));

      Axis horizontalAxis = new Axis (true, insets_);
      horizontalAxis.setStyle (axisStyle);
      add (horizontalAxis);
      
      Axis verticalAxis = new Axis (false, insets_);
      verticalAxis.setStyle (axisStyle);
      add (verticalAxis);
    }
    

    public void draw()
    {
      GViewport viewport = getScene().getViewport();

      int x0     = insets_.left;
      int y0     = insets_.top;
      int width  = viewport.getX3() - insets_.right - insets_.left + 1;
      int height = viewport.getY3() - insets_.bottom - insets_.top + 1;

      // Draw background
      background_.setGeometry (Geometry.createRectangle (x0, y0, width, height));

      // Draw title
      title_.setGeometry (x0 + width / 2, y0 / 2);
    }
  }



  /**
   * A GObject representing one axis with annotation.
   */   
  private class Axis extends GObject
  {
    private Insets   insets_;
    private boolean  isTop_;


    public Axis (boolean isTop, Insets insets)
    {
      isTop_  = isTop;
      insets_ = insets;
    }
    
    
    public void draw()
    {
      removeSegments();
      
      // Get device coordinates
      GViewport viewport = getScene().getViewport();

      int vx0 = insets_.left;
      int vy0 = insets_.top;
      int vx1 = viewport.getX3() - insets_.right;
      int vy1 = viewport.getY3() - insets_.bottom;

      // Get annotation range
      GObject plot = (GObject) getScene().getUserData();
      if (plot == null) return;
      
      GWorldExtent worldExtent = plot.getScene().getWorldExtent();

      double[] w0 = worldExtent.get (0);
      double[] w1 = worldExtent.get (1);
      double[] w2 = worldExtent.get (2);      

      // Prepare axis values
      double from = isTop_ ? w0[0] : w2[1];
      double to   = isTop_ ? w1[0] : w0[1];

      int x0 = vx0;
      int y0 = vy0;
      int x1 = isTop_ ? vx1 : x0;
      int y1 = isTop_ ? y0 : vy1;

      double length = Geometry.length (x0, y0, x1, y1);
      int n = (int) (length / 50.0);

      NiceNumbers niceNumbers = new NiceNumbers (from, to, n, true);      

      DecimalFormat format = new DecimalFormat ("0.00");
      
      for (Iterator i = niceNumbers.iterator(); i.hasNext(); ) {
        NiceNumber niceNumber = (NiceNumber) i.next();

        int rank = niceNumber.getRank();
        if (rank < 2) {
          int tickLength = rank == 0 ? 5 : 3;

          GSegment tick = new GSegment();
          int tx0 = isTop_ ? x0 + (int) Math.round (niceNumber.getPosition() * (x1 - x0)) : x0 - tickLength;
          int ty0 = isTop_ ? y0 - tickLength : y0 + (int) Math.round (niceNumber.getPosition() * (y1 - y0));
          int tx1 = isTop_ ? tx0 : (rank == 0 ? vx1 : x0);
          int ty1 = isTop_ ? (rank == 0 ? vy1 : y0) : ty0;
          tick.setGeometry (tx0, ty0, tx1, ty1);

          if (rank == 0) {
            double value = niceNumber.getValue();
            GText text = new GText (format.format (value),
                                    isTop_ ? GPosition.TOP : GPosition.LEFT);
            tick.setText (text);
          }

          addSegment (tick);
        }
      }
    }
  }
  


  private class Curve extends GObject
  {
    private GSegment  curve_;
    private double[]  values_;

    
    Curve (ValueSet valueSet)
    {
      curve_ = new GSegment();
      addSegment (curve_);

      int nValues = 400;
      values_ = new double[2*nValues];

      double[] xRange = valueSet.getXRange();

      double step = (xRange[1] - xRange[0]) / nValues;
      int index = 0;
      for (int i = 0; i < nValues; i++) {
        double x = xRange[0] + i*step;
        double y = valueSet.getY (x);

        values_[index + 0] = x;
        values_[index + 1] = y;

        index+=2;
      }
    }
    


    public void draw()
    {
      curve_.setGeometryXy (values_);
    }
  }
  
  
  
  /**
   * Defines the geometry and presentation for a sample
   * graphic object.
   */   
  private class Plot extends GScene
  {
    private Insets   insets_;

    
    Plot (GWindow window, ValueSet valueSet, Insets insets)
    {
      super (window);

      insets_ = insets;
      
      double[] xRange = valueSet.getXRange();
      double[] yRange = valueSet.getYRange();      
      
      double w0[] = {xRange[0], yRange[0], 0.0};
      double w1[] = {xRange[1], yRange[0], 0.0};
      double w2[] = {xRange[0], yRange[1], 0.0};
      setWorldExtent (w0, w1, w2);

      Curve curve = new Curve (valueSet);
      GStyle curveStyle = new GStyle();
      curveStyle.setForegroundColor (new Color (255, 0, 0));
      curveStyle.setLineWidth (2);
      curve.setStyle (curveStyle);
      add (curve);
    }


    protected void resize (double dx, double dy)
    {
      super.resize (dx, dy);
      setViewport (insets_.left, insets_.top,
                   getWindow().getWidth() - insets_.left - insets_.right,
                   getWindow().getHeight() - insets_.top - insets_.bottom);
    }
  }

  

  private class ValueSet
  {
    public double getMin()
    {
      return 0.0;
    }
    
    
    public double[] getXRange()
    {
      return new double[] {-100.0, +100.0};      
    }
    
    
    public double[] getYRange()
    {
      return new double[] {-1.0, +1.0};      
    }
    
    
    public double getY (double x)
    {
      double a = Math.sqrt (Math.abs (x));
      return Math.sin (x) / (a == 0.0 ? 1.0 : a);
    }
  }
  

  
  public static void main (String[] args)
  {
    new Demo15();
  }
}
